﻿using Qlab.SharedKernel;
using Qlab.SharedKernel.Exceptions;
using Qlab.SharedKernel.Interfaces;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Qlab.SharedKernel.Parameters;

namespace Qlab.Infrastructure.Data
{
    public abstract class EfRepository : IRepository
    {
        private readonly AppDbContext _dbContext;

        public EfRepository(AppDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public async Task AddAsync<T>(T entity) where T : BaseEntity, IAggregateRoot
        {
            await _dbContext.Set<T>().AddAsync(entity);
            await _dbContext.SaveChangesAsync();
        }

        public async Task DeleteAsync<T>(T entity) where T : BaseEntity, IAggregateRoot
        {
            _dbContext.Set<T>().Remove(entity);
            await _dbContext.SaveChangesAsync();
        }

        public async Task ExecuteTransAsync(Action<IRepository> action)
        {
            await using var transaction = await _dbContext.Database.BeginTransactionAsync();

            try
            {
                action.Invoke(this);
                await transaction.CommitAsync();
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync();
                throw new Exception(ex.Message);
            }
        }

        public async Task<T> GetByIdAsync<T>(int id) where T : BaseEntity, IAggregateRoot
        {
            var entity = await _dbContext.Set<T>().FindAsync(id);
            if (entity == null)
                throw new EntityNotFoundException();
            return entity;
        }

        public async Task<(int, List<TSource>)> PageListAsync<TSource, TKey>(PageListParameter<TSource, TKey> parameter) where TSource : BaseEntity, IAggregateRoot
        {
            int count = _dbContext.Set<TSource>().Where(parameter.Where).Count();
            var query = await _dbContext.Set<TSource>().Where(parameter.Where).Skip((parameter.PageIndex - 1) * parameter.PageSize)
                .Take(parameter.PageSize).OrderBy(parameter.OrderFields).OrderByDescending(parameter.OrderDescFields).ToListAsync();
            return (count, query);
        }

        public async Task<List<T>> QueryAsync<T>(Expression<Func<T, bool>> predicate = null) where T : BaseEntity, IAggregateRoot
        {
            return await _dbContext.Set<T>().Where(predicate).ToListAsync();
        }

        public async Task UpdateAsync<T>(T entity) where T : BaseEntity, IAggregateRoot
        {
            _dbContext.Set<T>().Update(entity);
            await _dbContext.SaveChangesAsync();
        }
    }
}